package drive

import (
	"fmt"
	"io/fs"
	"log"
	"my189go/pkg"
	"my189go/pkg/file"
	"os"
	"path"
	"path/filepath"
	"time"
)

func (client *FS) UploadFrom(file pkg.Upload) error {
	uploader := client.api.Uploader()
	return uploader.Write(file)
}
func (client *FS) Upload(cloud string, locals ...string) error {
	dir, err := client.stat(cloud)
	if len(locals) > 1 || os.IsNotExist(err) {
		client.Mkdir(cloud[1:])
		dir, _ = client.stat(cloud)
	}
	up := make([]pkg.Upload, 0)
	for _, local := range locals {
		if file.IsNetFile(local) {
			up = append(up, file.NewURLFile(dir.Id(), local))
			continue
		}
		if file.IsFastFile(local) {
			u := file.NewFastFile(dir.Id(), local)
			up = append(up, u)
			continue
		}
		files, _ := client.uploadLocal(dir, cloud, local)
		up = append(up, files...)
	}
	uploader := client.api.Uploader()
	for _, v := range up {
		err := uploader.Write(v)
		if err != nil {
			log.Println(err)
		}
	}
	return nil
}

/*
*
将本地文件或目录同步到网盘
同步逻辑
1、远端文件不存在，执行普通上传方法；
2、远端文件已存在，但是是大小不一致，执行上传
3、本地是目录，远程是文件，或者本地是文件，远端是目录，报错
4、本地是目录，远端也是目录，遍历递归本方法
*/
func (client *FS) UpSync(cloud string, local string) error {
	tryCount := 3
	localStat, err := os.Stat(local)
	if err != nil {
		return err
	}

	cloudStat, err := client.stat(cloud) // 检查远端文件是否存在
	//cloudNotExist := false
	if os.IsNotExist(err) {
		// 远端不存在,可以执行上传
		if !localStat.IsDir() {
			var upErr error
			for i := 1; i < tryCount; i++ {
				upErr = client.Upload(path.Dir(cloud), local) // 文件就直接调用上传方法
				if upErr == nil {
					break
				}
				fmt.Printf("-76 上传 %s 失败\n", local)
				time.Sleep(time.Second)
			}
			return upErr
		}
		// 如果是目录就需要遍历出文件再上传了
		localFiles, err := os.ReadDir(local)
		if err != nil {
			fmt.Println(err)
			return err
		}
		for _, v := range localFiles {
			fileName := v.Name()
			fmt.Println("-89 fileName:", fileName)

			if v.IsDir() {
				fmt.Printf("UpSync(%s, %s)\n", cloud+"/"+fileName, local+"/"+fileName)
				err := client.UpSync(cloud+"/"+fileName, local+"/"+fileName) // 目录就执行同步这个目录
				if err != nil {
					return err
				}
			} else {
				fmt.Printf("Upload(%s, %s)\n", cloud+"/"+fileName, local+"/"+fileName)

				var upErr error
				for i := 1; i < tryCount; i++ {
					upErr = client.Upload(cloud, local+"/"+fileName) // 文件就执行上传
					if upErr == nil {
						break
					}
					fmt.Printf("-106 上传 %s 失败\n", local)
					time.Sleep(time.Second)
				}
				if upErr != nil {
					return upErr
				}
			}
			time.Sleep(time.Second)
		}
		return nil
	}
	// 远端存在，需要比较再决定是否上传
	if localStat.IsDir() && !cloudStat.IsDir() {
		//fmt.Println("本地是目录，远端是文件")
		return fmt.Errorf("本地:%s 是目录，远端:%s 是文件", local, cloud)
	}
	if !localStat.IsDir() && cloudStat.IsDir() {
		//fmt.Println("本地是文件，远端是目录")
		return fmt.Errorf("本地:%s 是文件，远端:%s 是目录", local, cloud)
	}

	if !localStat.IsDir() {
		// 是文件，则比较文件大小是否一致
		if localStat.Size() == cloudStat.Size() {
			fmt.Printf("文件 %s 大小一致\n", local)
			return nil
		} else {

			var upErr error
			for i := 1; i < tryCount; i++ {
				upErr = client.Upload(path.Dir(cloud), local)
				if upErr == nil {
					break
				}
				fmt.Printf("-140 上传 %s 失败\n", local)
				time.Sleep(time.Second)
			}

			return upErr
		}
	}

	// 如果本地是目录，则遍历目录，分别同步子文件和子目录
	localFiles, err := os.ReadDir(local)
	if err != nil {
		fmt.Println(err)
		return err
	}
	for _, v := range localFiles {
		fileName := v.Name()
		fmt.Println("-156 fileName:", fileName)
		client.UpSync(cloud+"/"+fileName, local+"/"+fileName)
		time.Sleep(time.Second)
	}

	return nil
}

func (client *FS) uploadLocal(parent pkg.File, cloud, local string) ([]pkg.Upload, error) {
	stat, err := os.Stat(local)
	if err != nil {
		return nil, err
	}
	up := make([]pkg.Upload, 0)
	if !stat.IsDir() {
		up = append(up, file.NewLocalFile(parent.Id(), local))
		return up, nil
	}
	dirs := map[string]string{
		".": parent.Id(),
	}
	filepath.WalkDir(local, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() {
			rel := file.Rel(local, path)
			if rel == "." {
				return nil
			}
			f, _ := client.api.Mkdir(parent, rel)
			dirs[rel] = f.Id()
			return nil
		}
		dir, _ := filepath.Split(path)
		rel := file.Rel(local, dir)
		up = append(up, file.NewLocalFile(dirs[rel], path))
		return err
	})
	return up, nil
}
